package com.ktjy.gateway.predicate;

import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

/**
 * VipRoutePredicateFactory 自定义VIP路由断言工厂，用于判断VIP用户是否有权限访问VIP服务
 * 使用：predicates: - Vip=user, bobo
 *
 * @author LILIBO
 * @since 2021-06-24
 */
@Component // 注册为Bean才能被Spring容器管理
public class VipRoutePredicateFactory extends AbstractRoutePredicateFactory<VipRoutePredicateFactory.Config> {

    /**
     * Param key.
     */
    public static final String PARAM_KEY = "param";

    /**
     * Value key.
     */
    public static final String VALUE_KEY = "value";

    public VipRoutePredicateFactory() {
        super(Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList(PARAM_KEY, VALUE_KEY);
    }

    /**
     * 自定义VIP路由断言工厂断言规则
     *
     * @param config
     * @return
     */
    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        // 编写自定义VIP路由断言工厂，重写test方法，返回true表示断言通过，返回false表示断言失败
        return new GatewayPredicate() {
            @Override
            public boolean test(ServerWebExchange exchange) {
                String first = exchange.getRequest().getQueryParams().getFirst(config.param); // &user=bobo
                // 判断带有Predicate配置的参数名Vip中，param的值不为空并且value等于配置的值
                return StringUtils.hasText(first) && first.equals(config.value);
            }

            @Override
            public Object getConfig() {
                return config;
            }

            @Override
            public String toString() {
                return String.format("Vip: param=%s value=%s", config.getParam(), config.getValue());
            }
        };
    }

    @Data
    @Validated
    public static class Config {

        @NotEmpty
        private String param;
        @NotEmpty
        private String value;

    }

}
